package cn.itsub.sodo.timer;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import cn.itsub.sodo.core.Config;

/** 
	调度器：可以在预定的时间执行回调
	
	回调有两种：
		-- 更新回调：每一帧都会执行回调
		-- 自定义回调：指定间隔时间执行回调，

	推荐使用更新回调
*/
public class HiScheduler {

    // 用于记录"目标-回调-优先级"的双链表
    private static class tListEntry {
        public Method impMethod;
        public UpdateCallback callback;
        public Object	target;
        public int		priority;
        public boolean	paused;
    };

    // 用于记录"目标-计时-状态"
    private static class tHashSelectorEntry {
        ArrayList<HiTimer>    timers;
        Object			target;
        ArrayList<tListEntry> list;
        tListEntry 		entry;
        int	            timerIndex;
        HiTimer			currentTimer;
        boolean			currentTimerSalvaged;
        boolean			paused;
        void setPaused(boolean b){
            paused = b;
            if (entry != null){
                entry.paused = b;
            }
        }
    }

	//按优先级区分
	ArrayList<tListEntry>    updatesNeg;	// list priority < 0
	ArrayList<tListEntry>    updates0;		// list priority == 0
	ArrayList<tListEntry>    updatesPos;	// list priority > 0
		
	//按回调类型区分
	ConcurrentHashMap<Object, tHashSelectorEntry>  hashForSelectors;
	ConcurrentHashMap<Object, tHashSelectorEntry>  hashForUpdates;
    
    tListEntry							currentEntry;
	tHashSelectorEntry	                currentTarget;
	boolean						        currentTargetSalvaged;
	

	String				updateSelector;

    /** 
     * 时间流速，默认值1.0
     * 当值小于1时为慢动作，当值快于1时为快动作
     * @warning 它会影响所有的调度任务.
    */
    private float timeScale_;

    public float getTimeScale() {
        return timeScale_;
    }

    public void setTimeScale(float ts) {
        timeScale_ = ts;
    }

    private static HiScheduler _sharedScheduler = null;

    /** 返回单例的调度器 */
    public static HiScheduler getInstance() {
        if (_sharedScheduler != null) {
            return _sharedScheduler;
        }
        synchronized (HiScheduler.class) {
            if (_sharedScheduler == null) {
                _sharedScheduler = new HiScheduler();
            }
            return _sharedScheduler;
        }
    }

    /**
     * 清除单例的调度器
     */
    public static void purgeSharedScheduler() {
        _sharedScheduler = null;
    }

    private HiScheduler() {
        timeScale_ = 1.0f;

        // 触发时基于反射调用的方法名
        updateSelector = "update";

        // updates with priority
        updates0   = new ArrayList<tListEntry>();
        updatesNeg = new ArrayList<tListEntry>();
        updatesPos = new ArrayList<tListEntry>();
        hashForUpdates   = new ConcurrentHashMap<Object, tHashSelectorEntry>();
        hashForSelectors = new ConcurrentHashMap<Object, tHashSelectorEntry>();

        // selectors with interval
        currentTarget = null;
        currentTargetSalvaged = false;
    }


    /**
     * 滴答（你不应该调用这个方法，除非你知道你在做什么）
    */
    public void tick(float dt) {
        if( timeScale_ != 1.0f )
            dt *= timeScale_;
        
        currentTargetSalvaged = false;
        // 更新优先级<0的任务
        synchronized (updatesNeg) {
        	int len = updatesNeg.size();
        	
        	try {
        		for (int i = 0; i < len; i++) {
        			tListEntry e = updatesNeg.get(i);
        			currentEntry = e;
        			if( ! e.paused ) {
        				if(e.callback !=null) {
        					e.callback.update(dt);
        				} else {
        					try {
        						e.impMethod.invoke(e.target, dt);
        					} catch (InvocationTargetException e1) {
        						if(e1.getTargetException() instanceof RuntimeException)
        	        				throw (RuntimeException)e1.getTargetException();
        	        			else
        	        				e1.printStackTrace();
        					} catch (Exception e1) {
        						e1.printStackTrace();
        					}
        				}
        				if(currentTargetSalvaged) {
        					updatesNeg.remove(i);
        					i--;
        					len--;
        					currentTargetSalvaged = false;
        				}
        			}
        		}
        	} catch(IndexOutOfBoundsException e) {
        		e.printStackTrace();
        	}
        	
	        currentEntry = null;
        }

        // 更新优先级等于0的任务
        synchronized (updates0) {
        	int len = updates0.size();
        	
        	try {
        		for(int i=0; i < len; i++) {
        			tListEntry e = updates0.get(i);
        			currentEntry = e;
        			if( ! e.paused ) {
        				if(e.callback !=null) {
        					e.callback.update(dt);
        				} else {
        					try {
        						e.impMethod.invoke(e.target, dt);
        					} catch (Exception e1) {
        						e1.printStackTrace();
        					}
        				}
        				if(currentTargetSalvaged) {
        					updates0.remove(i);
        					i--;
        					len--;
        					currentTargetSalvaged = false;
        				}
        			}
        		}
        	} catch(IndexOutOfBoundsException e) {
        		e.printStackTrace();
        	}
        	
	        currentEntry = null;
        }
        
        // 更新优先级>0的任务
        synchronized (updatesPos) {
        	int len = updatesPos.size();
        	
        	try {
        		for (int i=0; i < len; i++) {
        			tListEntry e = updatesPos.get(i);
        			currentEntry = e;
        			if( ! e.paused ) {
        				if(e.callback !=null) {
        					e.callback.update(dt);
        				} else {
        					try {
        						e.impMethod.invoke(e.target, dt);
        					} catch (Exception e1) {
        						e1.printStackTrace();
        					}
        				}
        				if(currentTargetSalvaged) {
        					updatesPos.remove(i);
        					i--;
        					len--;
        					currentTargetSalvaged = false;
        				}
        			}
        		}
        	} catch(IndexOutOfBoundsException e) {
        		e.printStackTrace();
        	}
        	
	        currentEntry = null;
        }
        
        Iterator<Entry<Object, tHashSelectorEntry>> it = hashForSelectors.entrySet().iterator();
        while(it.hasNext()){
        	Entry<Object, tHashSelectorEntry> e = it.next();
        	tHashSelectorEntry elt = e.getValue();

        	currentTarget = elt;
            currentTargetSalvaged = false;

            if( ! currentTarget.paused && elt.timers != null) {
                // Timers数组可能在循环中更改。
                for( elt.timerIndex = 0; elt.timerIndex < elt.timers.size(); elt.timerIndex++) {
                    elt.currentTimer = elt.timers.get(elt.timerIndex);
                    elt.currentTimerSalvaged = false;

                    elt.currentTimer.update(dt);

                    if( elt.currentTimerSalvaged ) {
                        elt.currentTimer = null;
                    }
                }			
            }

	        // 如果没有行动计划则删除currentTarget
            if( currentTargetSalvaged && currentTarget.timers.isEmpty()) {
            	hashForSelectors.remove(elt.target);
            }
        }
        currentTarget = null;
        
    }

    static class SchedulerTimerAlreadyScheduled extends RuntimeException {
		private static final long serialVersionUID = -3460936783625533341L;
		public SchedulerTimerAlreadyScheduled(String reason) {
            super(reason);
        }
    }

    static class SchedulerTimerNotFound extends RuntimeException {
		private static final long serialVersionUID = -1912889437889458701L;
		public SchedulerTimerNotFound(String reason) {
            super(reason);
        }
    }

    /**
     * 每隔一段时间都会执行一次target对象的方法，selector用于指定方法名称，interval是间隔秒数，
     * 如果interval为0，则每一帧都会执行，如果这样，我们推荐使用'scheduleUpdateForTarget'
     */
    public void schedule(String selector, Object target, float interval, boolean paused) {
        assert selector != null: "Argument selector must be non-nil";
        assert target != null: "Argument target must be non-nil";	

        tHashSelectorEntry element = hashForSelectors.get(target);

        if( element == null ) {
            element = new tHashSelectorEntry();
            element.target = target;
            hashForSelectors.put(target, element);
            element.paused = paused;

        } else {
            assert element.paused == paused : "Scheduler. Trying to schedule a selector with a pause value different than the target";
        }

        if( element.timers == null) {
            element.timers = new ArrayList<HiTimer>();
        }
        HiTimer timer = new HiTimer(target, selector, interval);
        element.timers.add(timer);
    }
    
    /**
     * 向target对象添加计时任务，每隔一段时间都会执行一次callback，
     * interval是间隔秒数，如果interval为0，则每一帧都会执行
     */
    public void schedule(UpdateCallback callback, Object target, float interval, boolean paused) {
        assert callback != null: "Argument callback must be non-nil";
        assert target != null: "Argument target must be non-nil";	

        tHashSelectorEntry element = hashForSelectors.get(target);

        if( element == null ) {
            element = new tHashSelectorEntry();
            element.target = target;
            hashForSelectors.put(target, element);
            element.paused = paused;
        } else {
            assert element.paused == paused : "HiScheduler. Trying to schedule a selector with a pause value different than the target";
        }

        if( element.timers == null) {
            element.timers = new ArrayList<HiTimer>();
        }
        HiTimer timer = new HiTimer( callback, interval);
        element.timers.add(timer);
    }
    
    

    /**
     * 从目标上移除指定方法名的任务
    */
    public void unschedule(String selector, Object target) {
        if( target==null || selector==null)
            return;

        assert target != null: "Target MUST not be null";
        assert selector != null: "Selector MUST not be null";

        tHashSelectorEntry element = hashForSelectors.get(target);
        if( element != null ) {
            for( int i=0; i< element.timers.size(); i++ ) {
                HiTimer timer = element.timers.get(i);

                if(selector.equals(timer.getSelector())) {
                    if( timer == element.currentTimer && !element.currentTimerSalvaged ) {                        
                        element.currentTimerSalvaged = true;
                    }
                    	
                    element.timers.remove(i);

                    if( element.timerIndex >= i )
                        element.timerIndex--;

                    if( element.timers.isEmpty()) {
                        if( currentTarget == element ) {
                            currentTargetSalvaged = true;						
                        } else {
                        	hashForSelectors.remove(element.target);
                        }
                    }
                    return;
                }
            }
        }

    }
    
    /*
     * 从目标上移除callback任务
     */
    public void unschedule(UpdateCallback callback, Object target) {
        // 显式地处理零参数去除对象时
        if( target==null || callback==null)
            return;

        assert target != null: "Target MUST not be null";
        assert callback != null: "Selector MUST not be null";

        tHashSelectorEntry element = hashForSelectors.get(target);
        if( element != null ) {
            for( int i=0; i< element.timers.size(); i++ ) {
                HiTimer timer = element.timers.get(i);

                if(callback == timer.getCallback()) {
                    if( timer == element.currentTimer && !element.currentTimerSalvaged ) {                        
                        element.currentTimerSalvaged = true;
                    }
                    	
                    element.timers.remove(i);

                    if( element.timerIndex >= i )
                        element.timerIndex--;

                    if( element.timers.isEmpty()) {
                        if( currentTarget == element ) {
                            currentTargetSalvaged = true;						
                        } else {
                        	hashForSelectors.remove(element.target);
                        }
                    }
                    return;
                }
            }
        }

    }

    /**
     * 注销目标身上的Update任务
     */
    public void unscheduleUpdate(Object target) {
        if( target == null )
            return;
        tHashSelectorEntry entry = hashForUpdates.get(target);
        if ( entry == null )
        	return;

        synchronized (entry.list) {
        	if(currentEntry==entry.entry) {
        		currentTargetSalvaged = true;
        	} else {
        		entry.list.remove(entry.entry);
        	}
		}
        
        hashForUpdates.remove(target);
    }

    /**
     * 注销目标所有的任务
    */
	public void unscheduleAllSelectors(Object target) {

        if( target == null )return;

        // 注销'自定义选择器'
        tHashSelectorEntry element = hashForSelectors.get(target);
        if( element != null) {
            if(!element.currentTimerSalvaged ) {
                element.currentTimerSalvaged = true; //标记为可移除
            }
            element.timers.clear();
            if( currentTarget == element )
                currentTargetSalvaged = true; //标记为可移除
            else {
            	hashForSelectors.remove(element.target);
            }
        }

        // 注销'Update选择器'
        this.unscheduleUpdate(target);
	}

    /**
     * 取消所有调度任务（不要调用这个方法，除非你知道自己在做什么）
      */
    public void unscheduleAllSelectors() {
        // 遍历'自定义选择器'
    	Iterator<Entry<Object, tHashSelectorEntry>> it = hashForSelectors.entrySet().iterator();
    	while(it.hasNext()){
    		Entry<Object, tHashSelectorEntry> e = it.next();
    		tHashSelectorEntry element = e.getValue();
            Object target = element.target;
            unscheduleAllSelectors(target);
    	}
       
        // 遍历'Update选择器'
        for (tListEntry entry:updates0) {
        	unscheduleUpdate(entry.target);
        }
        for (tListEntry entry:updatesNeg) {
        	unscheduleUpdate(entry.target);
        }
        for (tListEntry entry:updatesPos) {
        	unscheduleUpdate(entry.target);
        }
    }

    /**
     * 恢复目标的调度任务，如果参数为空则什么也不发生
    */
	public void resume(Object target) {
        assert  target != null: "target must be non nil";

        // Custom Selectors
        tHashSelectorEntry element = hashForSelectors.get(target);
        if( element != null )
            element.paused = false;

        // Update selector
        tHashSelectorEntry elementUpdate = hashForUpdates.get(target);
        if( elementUpdate != null) {
            assert elementUpdate.target != null: "resumeTarget: unknown error";
            elementUpdate.setPaused(false);
        }	

	}

    /**
     * 暂停目标的调度任务，如果参数为空则什么也不发生
    */
	public void pause(Object target) {
        assert target != null: "target must be non nil";

        // Custom selectors
        tHashSelectorEntry element = hashForSelectors.get(target);
        if( element != null )
            element.paused = true;

        // Update selectors
        tHashSelectorEntry elementUpdate = hashForUpdates.get(target);
        if( elementUpdate != null) {
            assert elementUpdate.target != null:"pauseTarget: unknown error";
            elementUpdate.setPaused(true);
        }

    }

    /**
     * 给目标注册一个指定优先级的任务。每一帧都会调用目标的update方法。优先级越低，它被调用的越早。
     * @param target	目标
     * @param priority	优先级
     * @param paused	是否暂停
     */
	public void scheduleUpdate(Object target, int priority, boolean paused) {
        if (Config.DEBUG) {
        	tHashSelectorEntry hashElement = hashForUpdates.get(target);
            assert hashElement == null:"Scheduler: You can't re-schedule an 'update' selector'. Unschedule it first";
        }

        // 大部分的更新都是0
        if( priority == 0 ) {
        	this.append(updates0, target, paused);
        } else if( priority < 0 ) {
        	this.priority(updatesNeg, target, priority, paused);
        } else { // priority > 0
        	this.priority(updatesPos, target, priority, paused);
        }
	}
	
	/**
     * 给目标注册一个指定优先级的任务。每一帧都会调用目标的update方法。优先级越低，它被调用的越早。
     * @param target	目标
     * @param priority	优先级
     * @param paused	是否暂停
     */
	public void scheduleUpdate(UpdateCallback target, int priority, boolean paused) {
        if (Config.DEBUG) {
        	tHashSelectorEntry hashElement = hashForUpdates.get(target);
            assert hashElement == null:"HiScheduler: You can't re-schedule an 'update' selector'. Unschedule it first";
        }

        if( priority == 0 ) {
        	this.append(updates0, target, paused);
        } else if( priority < 0 ) {
        	this.priority(updatesNeg, target, priority, paused);
        } else { // priority > 0
        	this.priority(updatesPos, target, priority, paused);
        }
	}


    @Override
    public void finalize () throws Throwable  {
        unscheduleAllSelectors();
        _sharedScheduler = null;

        super.finalize();
    }

    public void append(ArrayList<tListEntry> list, Object target, boolean paused) {
        tListEntry listElement = new tListEntry();

        listElement.target = target;
        listElement.paused = paused;
        if(target instanceof UpdateCallback) {
        	listElement.callback = (UpdateCallback)target;
        } else {
            try {
    			listElement.impMethod = target.getClass().getMethod(updateSelector, Float.TYPE);
            } catch (NoSuchMethodException e) {
        		e.printStackTrace();
        	}       	
        }

		synchronized (list) {
			list.add(listElement);			
		}

        tHashSelectorEntry hashElement = new tHashSelectorEntry();
        hashElement.target = target;
        hashElement.list = list;
        hashElement.entry = listElement;
        hashForUpdates.put(target, hashElement);
    }

    public void priority(ArrayList<tListEntry> list, Object target, int priority, boolean paused) {
        tListEntry listElement = new tListEntry();

        listElement.target = target;
        listElement.priority = priority;
        listElement.paused = paused;
        if(target instanceof UpdateCallback) {
        	listElement.callback = (UpdateCallback)target;
        } else {
	        try {
				listElement.impMethod = target.getClass().getMethod(updateSelector, Float.TYPE);
	        } catch (NoSuchMethodException e) {
        		e.printStackTrace();
        	}
        }
		
		synchronized (list) {
			if(list.isEmpty()) {
				list.add(listElement);
			} else {
				boolean added = false;		
				
				int len = list.size();
				for( int i = 0; i < len; i++ ) {
					tListEntry elem = list.get(i);
					if( priority < elem.priority ) {
						list.add(i, listElement);
						added = true;
						break;
					}
				}
				
				// Not added? priority has the higher value. Append it.
				if( !added )
					list.add(listElement);
			}
		}

        tHashSelectorEntry hashElement = new tHashSelectorEntry();
        hashElement.target = target;
        hashElement.list = list;
        hashElement.entry = listElement;  
        hashForUpdates.put(target, hashElement);
    }
}

